package com.stone.serverless.core.classloader;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.URLUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.stone.serverless.core.classloader.config.ComponentBizConfig;
import com.stone.serverless.core.classloader.config.ComponentClassConfig;
import com.stone.serverless.core.classloader.config.ComponentEnvironmentConfig;
import com.stone.serverless.core.classloader.statistics.ModuleStatistics;
import com.stone.serverless.core.classloader.utils.JarFileUtil;
import com.stone.serverless.core.classloader.utils.ResolveServiceUtil;
import com.stone.serverless.sdk.constant.ClassPathConstant;
import com.stone.serverless.sdk.constant.EnvironmentConstant;
import com.stone.serverless.sdk.domain.ServiceInfo;
import com.stone.serverless.sdk.enums.RuntimeTypeEnum;
import com.stone.serverless.sdk.event.ComponentRefresh;
import com.stone.serverless.sdk.event.ComponentRunner;
import com.stone.serverless.sdk.loader.JarLoadService;
import com.stone.serverless.sdk.loader.ModuleClassLoader;
import com.stone.serverless.sdk.rumtime.ContainerRuntime;
import com.stone.serverless.sdk.utils.LambdaUtil;
import lombok.SneakyThrows;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

/**
 * @Description: TODO
 * @Author: MECHREVO
 * @Date: 2022/1/13 9:58
 * @Version: 1.0.0
 */
public class BizJarModuleClassLoader extends JarModuleClassLoader {

    private ComponentBizConfig componentBizConfig;

    private ComponentClassConfig componentClassConfig;

    private ComponentEnvironmentConfig componentEnvironmentConfig;

    //持有组件加载器
    private JarLoadService jarLoadService;

    public BizJarModuleClassLoader(File file, ClassLoader parentClassLoader, JarLoadService jarLoadService) {
        super(parentClassLoader);
        String prefix = FileUtil.getPrefix(file);
        System.out.println("load module " + prefix);
        this.componentBizConfig = new ComponentBizConfig(URLUtil.getURL(file).toString(), prefix);
        this.componentClassConfig = new ComponentClassConfig();
        this.jarLoadService = jarLoadService;
        this.initLoader(file);
    }

    /**
     * 初始化类加载器
     */
    @Override
    @SneakyThrows
    protected void initLoader(File file) {
        JarFile jarFile = new JarFile(file);
        //System.out.println("start initEnvironment");
        //初始化变量
        this.initEnvironment(jarFile);
        //System.out.println("start deleteFileJar");
        //删除临时文件
        JarFileUtil.delete(this.componentBizConfig.getComponentName(),
                this.componentBizConfig.getVersion());
        super.initLoader(file);
    }

    private void initEnvironment(JarFile jarFile) {
        Enumeration<JarEntry> en = jarFile.entries();
        while (en.hasMoreElements()) {
            InputStream input = null;
            String name = null;
            try {
                JarEntry je = en.nextElement();
                name = je.getName();
                input = jarFile.getInputStream(je);
                //加载环境变量
                this.loadEnvironment(name, input);
            } catch (IOException e) {
                System.out.println("加载文件" + name + "失败," + e.getMessage());
            } finally {
                IoUtil.close(input);
            }
        }

    }

    @Override
    public String getUrl() {
        return this.componentBizConfig.getUrl();
    }

    @Override
    public Object getComponentRunner() {
        return this.componentEnvironmentConfig.getComponentRunner();
    }

    @Override
    public <T> T getEnvironment(String key) {
        return this.componentEnvironmentConfig.getEnvironment(key);
    }


    //重写loadClass方法
    //改写loadClass方式
    @Override
    public Class<?> loadClass(String name) throws ClassNotFoundException {
        Class<?> aClass = this.componentClassConfig.getCacheAllClassMap().get(name);
        if (ObjectUtil.isNotNull(aClass)) {
            return aClass;
        }

        aClass = super.loadClass(name);

        if (ObjectUtil.isNull(aClass)) {
            return aClass;
        }
        this.componentClassConfig.getCacheAllClassMap().put(name, aClass);

        String basePackage = this.componentBizConfig.getBasePackage();
        if (ObjectUtil.isNotNull(basePackage)) {
            if (name.startsWith(basePackage)) {
                this.componentClassConfig.getCacheComponentClassMap().put(name, aClass);
            } else {
                this.componentClassConfig.getCacheFrameworkClassMap().put(name, aClass);
            }
        }
        return aClass;
    }

    @Override
    public String getVersion() {
        return this.componentBizConfig.getVersion();
    }


    @Override
    public List<ServiceInfo> getServices() {
        return this.componentBizConfig.getServices();
    }

    @Override
    public Object getComponentConsumer() {
        return this.componentEnvironmentConfig.getComponentConsumer();
    }

    @Override
    public Object getComponentProvider() {
        return this.componentEnvironmentConfig.getComponentProvider();
    }

    @Override
    public List<String> routerStatistics() {
        return ModuleStatistics.routerStatistics(this);
    }


    @Override
    public List<Map<String, String>> servicesStatistics() {
        return ModuleStatistics.servicesStatistics(this);
    }

    /**
     * 获取容器内的实例对象
     *
     * @param className
     * @return
     */
    @Override
    public Object getInstance(String className) {
        ContainerRuntime containerRuntime = this.componentEnvironmentConfig.getContainerRuntime();
        if (ObjectUtil.isNull(containerRuntime)) {
            return null;
        }
        return containerRuntime.getInstance(className, this);
    }

    @Override
    public JarLoadService getJarLoadService() {
        return this.jarLoadService;
    }

    @SneakyThrows
    @Override
    public void publishInstalled() {
        //解析外部服务
        //通知其他服务 告知我已经加载成功了
        List<ServiceInfo> services = this.getServices();
        if (CollUtil.isEmpty(services)) {
            return;
        }
        JarLoadService jarLoadService = this.getJarLoadService();
        for (ServiceInfo serviceInfo : services) {
            ModuleClassLoader jarModuleClassLoader = jarLoadService.getJarModuleClassLoader(
                    serviceInfo.getComponentName(), serviceInfo.getVersion());
            if (ObjectUtil.isNull(jarModuleClassLoader)) {
                continue;
            }
            //找runnner
            Object componentRunner = jarModuleClassLoader.getComponentRunner();
            if (ObjectUtil.isNull(componentRunner)) {
                continue;
            }


            ReflectUtil.invoke(componentRunner,
                    LambdaUtil.getMethodName(ComponentRunner::componentInstalled), this.getClassloader());
        }
    }

    @Override
    public void loaderComponentClasses() {
        //将jar中的每一个class字节码进行Class载入
        for (Map.Entry<String, byte[]> entry : classBytesMap.entrySet()) {
            String key = entry.getKey();
            this.loadBizComponent(key);
        }
    }

    @Override
    public void callRunner() {
        this.runner();
    }

    /**
     * 方法描述 初始化类加载器，保存字节码
     *
     * @param jarFile
     * @method init
     */
    @Override
    @SneakyThrows
    protected void unzipJar(JarFile jarFile) {
        //先解压
        JarFileUtil.unpackToFolder(jarFile, this.componentBizConfig.getComponentName(),
                this.componentBizConfig.getVersion());
        super.unzipJar(jarFile);
    }

    @SneakyThrows
    @Override
    public void loadBizComponent(String aClass) {
        if (!CharSequenceUtil.contains(aClass, this.componentBizConfig.getBasePackage())) {
            if (!CharSequenceUtil.startWith(aClass, this.componentBizConfig.getBasePackage())) {
                return;
            }
        }


        Class<?> cc = this.loadClass(aClass);
        if (ObjectUtil.isNull(cc)) {
            return;
        }
        this.componentEnvironmentConfig.loadComponentProvider(cc, this);
        this.componentEnvironmentConfig.loadComponentConsumer(cc, this);
        this.componentEnvironmentConfig.loadComponentRefresh(cc, this);
        this.componentEnvironmentConfig.loadComponentRunner(cc, this);
        this.componentEnvironmentConfig.loadComponentInvocationAfterHandler(cc, this);
        this.componentEnvironmentConfig.loadComponentInvocationBeforeHandler(cc, this);

    }

    @Override
    public File getUnzipJarFile(String jarFileName) {
        File jarFile = JarFileUtil.getFile(jarFileName, this.componentBizConfig.getComponentName(),
                this.componentBizConfig.getVersion());
        return jarFile;
    }

    /**
     * 加载环境变量
     *
     * @param name
     * @param inputStream
     */
    public void loadEnvironment(String name, InputStream inputStream) {
        if (!name.contains("component.json")) {
            return;
        }
        String s = IoUtil.readUtf8(inputStream);
        JSONObject jsonObject = JSONUtil.parseObj(s);
        Map<String, Object> environment = jsonNullProcessor(jsonObject);
        this.componentEnvironmentConfig = new ComponentEnvironmentConfig(environment);
        String componentName = this.getEnvironment("componentName");
        //如果自定义了组件名称
        if (ObjectUtil.isNotNull(componentName)) {
            this.componentBizConfig.setComponentName(componentName);
        }
        String version = this.getEnvironment("version");
        if (version != null) {
            this.componentBizConfig.setVersion(version);
        }
        String runtimeType = this.getEnvironment("runtimeType");
        if (StrUtil.isNotBlank(runtimeType)) {
            this.componentBizConfig.setRuntimeType(RuntimeTypeEnum.of(runtimeType));
        }
        String basePackage = this.componentEnvironmentConfig.getEnvironment("basePackage");
        this.componentBizConfig.setBasePackage(basePackage);

        JSONArray services = this.componentEnvironmentConfig.getEnvironment("services");
        if (CollUtil.isNotEmpty(services)) {
            this.componentBizConfig.setServices(JSONUtil.toList(services, ServiceInfo.class));
        }
    }

    /**
     * 解析依赖
     */
    @Override
    @SneakyThrows
    public void resolveDependencies() {
        ResolveServiceUtil.resolveDependencies(this);
    }


    /**
     * 初始化容器
     *
     * @method initBean
     */
    @Override
    public void initContainer() {
        RuntimeTypeEnum runTimeType = this.componentBizConfig.getRuntimeType();
        if (ObjectUtil.isNull(runTimeType)) {
            return;
        }
        Class<ContainerRuntime> containerRuntime = ContainerRuntime.get(runTimeType);
        if (ObjectUtil.isNull(containerRuntime)) {
            return;
        }
        this.componentEnvironmentConfig.setContainerRuntime(ReflectUtil.newInstance(containerRuntime));
        this.componentEnvironmentConfig.getContainerRuntime().initContainer(this);
    }


    @Override
    public String getComponentName() {
        return this.componentBizConfig.getComponentName();
    }

    @Override
    public String getDescription() {
        return this.componentBizConfig.getDescription();
    }


    /**
     * 卸载
     */
    @Override
    public void uninstall() {
        //卸载其他jar引入了这个服务的类 这里要注意的是如果目标引用的是rpc调用 那么这里不能去卸载
        //已经不需要卸载了因为采用了新的调用机制
        //jarLoadService.getJarModuleClassLoaders().forEach(v -> v.uninstallServices(this.getComponentName(), this.getVersion()));
        String componentName = this.getComponentName();
        String version = this.componentBizConfig.getVersion();
        // this.resolveServiceDep.uninstall(componentName, version, this);
        this.publishUninstalled();

        this.componentBizConfig.uninstall();
        this.componentEnvironmentConfig.uninstall();
        this.componentClassConfig.uninstall();
        this.componentClassConfig = null;
        this.componentBizConfig = null;
        this.componentEnvironmentConfig = null;
        this.jarLoadService = null;
        super.uninstall();
        //删除其他jar
        JarFileUtil.delete(componentName, version);

    }

    @SneakyThrows
    public void publishUninstalled() {
        //通知其他服务 告知我已经卸载了
        List<ServiceInfo> services = this.getServices();
        if (CollUtil.isEmpty(services)) {
            return;
        }
        JarLoadService jarLoadService = this.getJarLoadService();
        for (ServiceInfo serviceInfo : services) {
            ModuleClassLoader jarModuleClassLoader = jarLoadService.getJarModuleClassLoader(
                    serviceInfo.getComponentName(), serviceInfo.getVersion());
            if (ObjectUtil.isNull(jarModuleClassLoader)) {
                continue;
            }
            //找runnner
            Object componentRunner = jarModuleClassLoader.getComponentRunner();
            if (ObjectUtil.isNull(componentRunner)) {
                continue;
            }
            ReflectUtil.invoke(componentRunner,
                    LambdaUtil.getMethodName(ComponentRunner::componentUninstalled), this.getClassloader());
        }
    }
    @Override
    @SneakyThrows
    public void uninstallServices(String componentName, String version) {
        ResolveServiceUtil.uninstallDependOutService(componentName, version, this);
    }

    @Override
    @SneakyThrows
    public void registerRouter() {
        //如果不是webServer 则判断是否有要注册到webServer的控制器
        //判断是否依赖了web服务器
        List<ServiceInfo> services = this.getServices();
        if (CollUtil.isEmpty(services)) {
            return;
        }
        long count = services.stream().filter(v -> v.getClassPath().equals(ClassPathConstant.WEB_SERVER_CLASS_PATH)).count();
        //如果没有依赖
        if (count <= 0) {
            return;
        }
        ContainerRuntime containerRuntime = this.componentEnvironmentConfig.getContainerRuntime();
        if (ObjectUtil.isNull(containerRuntime)) {
            return;
        }
        List<Object> routerInfo = containerRuntime.getRouters(this);

        if (CollUtil.isEmpty(routerInfo)) {
            return;
        }
        Object service = ResolveServiceUtil.findDependOutService(
                this, ClassPathConstant.WEB_SERVER_CLASS_PATH);
        if (null == service) {
            return;
        }
        ReflectUtil.invoke(service, "registerRouters", routerInfo, this);
    }

    @Override
    public void refresh() {
        //如果没有启动 这里直接启动
        if (ObjectUtil.isNotNull(this.componentEnvironmentConfig.getContainerRuntime())) {
            this.componentEnvironmentConfig.getContainerRuntime().refresh();
        }
        /**
         * 调用自定义刷新策略
         */
        Object componentRefresh = this.componentEnvironmentConfig.getComponentRefresh();
        if (ObjectUtil.isNotNull(componentRefresh)) {

            ReflectUtil.invoke(componentRefresh, LambdaUtil.getMethodName(ComponentRefresh::refresh));
        }
    }

    public void runner() {
        Object componentRunner = this.componentEnvironmentConfig.getComponentRunner();
        if (ObjectUtil.isNotNull(componentRunner)) {
            /**
             * 配置信息
             */
            Map<String, Object> environment = this.componentEnvironmentConfig.getEnvironment("environment");
            if (ObjectUtil.isNull(environment)) {
                environment = new HashMap<>();
            }
            ReflectUtil.invoke(componentRunner,
                    LambdaUtil.getMethodName(ComponentRunner::run), environment, this.componentBizConfig.getBasePackage());
        }
    }
}
